Skip to content

feat(capy): add explicit OAuth reply permits for native bindings - #113

Merged
kjgbot merged 1 commit into
mainfrom
feat/capy-grant-permits
Nov 11, 2026
Merged

kjgbot merged 1 commit into
mainfrom
feat/capy-grant-permits

Conversation

@kjgbot

@kjgbot kjgbot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

An OAuth connection cannot reply as a native Capy binding solely because the same person owns both. Add an explicit send permit: bind_capy_thread(thread_id) returns an existing owned binding with already_bound: true and creates only an exact-grant permit. It refuses missing bindings. The binding retains its identity, conversations and native custody; the consented OAuth persona stays pinned independently.

The optional module exposes bind/unbind only to OAuth MCP contexts. Only send_relay_message as=<binding agent> resolves a permit; inbox, conversation, channel, join, launch and Desktop rights retain their existing actor checks. unbind_capy_thread revokes only this connection's permit. Grant revocation revokes its permits in the same transaction, while native inbound delivery remains independent. Use checks live membership/key ownership, grant/bearer, binding generation, immutable remote ID/name and admitted Relay route/key, including a fresh fence after message metadata reads.

This is an internal source slice for #99. The application does not mount the module; runtime capability remains false. Durable callbacks/subscriptions/delivery, history and real-account acceptance remain open. It changes neither generic OAuth assertActor/pin nor provider identity custody. Provider credentials remain exclusively in remote_agent_connections.

Migration 0046_capy_grant_permits.sql has tenant-bound foreign keys and one live permit per grant/binding. Fresh main 4d4a427f ends at0045; no other open Control PR reserved a migration immediately before this push. Completed native custody cleanup cascades only its permit records.

Validation (Node22.23.2 through aw-heavy, serial):

  • node node_modules/typescript/bin/tsc -p tsconfig.json --noEmit: clean.
  • node node_modules/typescript/bin/tsc -p tsconfig.build.json: clean.
  • Focused Capy/OAuth/migration regression:70 pass,0 fail,1 existing real-Postgres skip. The new13 permit checks also pass in the current full serial run, including refreshed bearer reuse, SQL tenant/unique constraints and completed cleanup.
  • Nine separate guard removals make their named tests fail: send-only seam, exact-grant isolation, permit revocation, live bearer, generation snapshot, immutable-ID snapshot, post-verification fence, pre-write fence, atomic grant revocation. Source was restored exactly after each control.
  • node --import tsx --test --test-concurrency=1 test/*.test.ts:1,197 pass,0 fail,0 cancelled,13 existing skips on Node22.23.2. Typecheck and build completed successfully immediately before this full run.
  • No real provider, deployment, grant mint, flag or permission action. Offline transport acceptance and green CI cannot establish runtime availability or provider delivery.

Admission contract: #99 comment6792521034. Callback ownership is Control messaging; provider-file/integration intake remains with relayfile-control.


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.

@coderabbitai

coderabbitai Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Pre-merge checks: Inconclusive 2

Failed Failed checks: 2 inconclusive

Check name Status Explanation Resolution
Title check Pending This assessment has not completed. The check will be retried on the next pre-merge check run.
Description check Pending This assessment has not completed. The check will be retried on the next pre-merge check run.
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@agent-relay-code agent-relay-code Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE on bdbda98 (review-only: I do not merge).

Contract check against issue #99 (comment 6792521034), all met:

  • Separate permit row (capy_grant_permits, migration 0046), not a grant id on the binding. Composite FKs target the existing UNIQUE on mcp_oauth_grants (0015, kept through the 0033 rename) and the new UNIQUE on capy_bindings; live uniqueness per (grant, binding).
  • Created only by bind_capy_thread under the OAuth grant, inside connection.lock(tx) plus the binding lock, for an already spawn-bound thread with same user/org/workspace, state='bound', owner/admin role and key-owner checks (via access() in current()), name derived from the thread id. No provider or Relay call on bind.
  • Used for send only through the #110 capyActor path; no participant row, join, pin or token exposure. assertActor/pin untouched; the only new branch in hostedActor is gated on OAuth + capyPermits + as !== approved persona, and a missing permit falls back to the original persona path (native Capy fallback still requires !managed && !oauth).
  • Revoked in the grant revocation transaction; fails closed on retire, generation bump, remote id or name change, key-owner change, role downgrade, expired/rotated bearer, and permit revocation between pre-send reads and the write.
  • No custody transfer; unbound-thread grant bind stays out of scope; default-off (module not supplied by the app server, capability false).

Local checks (one worker, serial), head bdbda98: typecheck 0; test/capy-grant-permits.test.ts 13/13; test/capy-bindings.test.ts 23/23; test/capy-mcp-identity.test.ts 6/6.
Exact-head CI: run 43005455197 success on bdbda98.

🤖 Generated with Claude Code

@kjgbot

kjgbot commented Nov 11, 2026

Copy link
Copy Markdown
Contributor Author

Independent exact-head approval5857520055 verifies the permit contract and independently runs13 permit,23 binding and6 native-identity checks with clean typecheck. Required CI43005455197 is SUCCESS on bdbda98;0 unresolved threads. CodeRabbit's visible title/description assessments are inconclusive with no code finding or location; its check is skipped, so no rerun or hidden-review gate is inferred.

Author validation is1,197 pass,0 fail/cancel,13 existing skips under Node22.23.2, clean typecheck/build, and nine named guard removals that each cause the intended regressions before exact restoration. These are source checks: the optional module remains unmounted and no provider runtime or E2E result is claimed. Remaining delivery/history/rehearsal work stays in #99.

@kjgbot
kjgbot merged commit 3d7e85e into main Nov 11, 2026
3 checks passed
@kjgbot
kjgbot deleted the feat/capy-grant-permits branch November 11, 2026 10:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant